@ecency/render-helper 2.5.11 → 2.5.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/index.d.ts +15 -1
- package/dist/browser/index.js +114 -7
- package/dist/browser/index.js.map +1 -1
- package/dist/node/index.cjs +114 -6
- package/dist/node/index.cjs.map +1 -1
- package/dist/node/index.mjs +114 -7
- package/dist/node/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/browser/index.d.ts
CHANGED
|
@@ -133,6 +133,20 @@ declare function setCacheSize(size: number): void;
|
|
|
133
133
|
|
|
134
134
|
declare const SECTION_LIST: string[];
|
|
135
135
|
|
|
136
|
+
/**
|
|
137
|
+
* True iff `value` is an absolute https:// URL whose host is one the renderer
|
|
138
|
+
* is permitted to embed AND (for hosts with a known embed-path shape) whose
|
|
139
|
+
* path matches that shape. Used to validate data-embed-src / data-video-href in
|
|
140
|
+
* the sanitizer and the iframe `src` the client video extensions assign.
|
|
141
|
+
*
|
|
142
|
+
* Defensive: never throws (a malformed URL returns false), rejects every
|
|
143
|
+
* non-https scheme (javascript:, data:, http:, protocol-relative //host),
|
|
144
|
+
* compares the parsed hostname so an attacker can't smuggle an allowed host as
|
|
145
|
+
* a path/query/userinfo/subdomain-suffix segment, and constrains the path so an
|
|
146
|
+
* allowed host can't be aimed at a non-embed route.
|
|
147
|
+
*/
|
|
148
|
+
declare function isAllowedEmbedSrc(value?: string | null): boolean;
|
|
149
|
+
|
|
136
150
|
declare function isValidPermlink(permlink: string): boolean;
|
|
137
151
|
|
|
138
152
|
/**
|
|
@@ -145,4 +159,4 @@ declare function isValidPermlink(permlink: string): boolean;
|
|
|
145
159
|
*/
|
|
146
160
|
declare function simpleMarkdownToHTML(input: string): string;
|
|
147
161
|
|
|
148
|
-
export { type Entry, IMAGE_SIZES, type ProxifyOptions, type RenderOptions, SECTION_LIST, type SeoContext, buildPictureSources, buildSrcSet, buildSrcSetForFormat, catchPostImage, getEntryImageRawUrl, isPictureEligibleRawUrl, isValidPermlink, getPostBodySummary as postBodySummary, proxifyImageSrc, markdown2Html as renderPostBody, setCacheSize, setProxyBase, setSlowRenderThresholdMs, simpleMarkdownToHTML };
|
|
162
|
+
export { type Entry, IMAGE_SIZES, type ProxifyOptions, type RenderOptions, SECTION_LIST, type SeoContext, buildPictureSources, buildSrcSet, buildSrcSetForFormat, catchPostImage, getEntryImageRawUrl, isAllowedEmbedSrc, isPictureEligibleRawUrl, isValidPermlink, getPostBodySummary as postBodySummary, proxifyImageSrc, markdown2Html as renderPostBody, setCacheSize, setProxyBase, setSlowRenderThresholdMs, simpleMarkdownToHTML };
|
package/dist/browser/index.js
CHANGED
|
@@ -176,6 +176,96 @@ var ALLOWED_ATTRIBUTES = {
|
|
|
176
176
|
"del": [],
|
|
177
177
|
"ins": []
|
|
178
178
|
};
|
|
179
|
+
|
|
180
|
+
// src/consts/embed-hosts.const.ts
|
|
181
|
+
var ALLOWED_EMBED_HOSTS = /* @__PURE__ */ new Set([
|
|
182
|
+
// YouTube
|
|
183
|
+
"www.youtube.com",
|
|
184
|
+
"youtube.com",
|
|
185
|
+
"www.youtube-nocookie.com",
|
|
186
|
+
"youtube-nocookie.com",
|
|
187
|
+
// Vimeo
|
|
188
|
+
"player.vimeo.com",
|
|
189
|
+
// Twitch
|
|
190
|
+
"player.twitch.tv",
|
|
191
|
+
// DTube
|
|
192
|
+
"emb.d.tube",
|
|
193
|
+
// 3Speak (video + audio)
|
|
194
|
+
"play.3speak.tv",
|
|
195
|
+
"3speak.tv",
|
|
196
|
+
"audio.3speak.tv",
|
|
197
|
+
// Loom
|
|
198
|
+
"www.loom.com",
|
|
199
|
+
// Spotify
|
|
200
|
+
"open.spotify.com",
|
|
201
|
+
// SoundCloud
|
|
202
|
+
"w.soundcloud.com",
|
|
203
|
+
// BitChute
|
|
204
|
+
"www.bitchute.com",
|
|
205
|
+
"bitchute.com",
|
|
206
|
+
// Rumble
|
|
207
|
+
"www.rumble.com",
|
|
208
|
+
"rumble.com",
|
|
209
|
+
// Brighteon
|
|
210
|
+
"www.brighteon.com",
|
|
211
|
+
"brighteon.com",
|
|
212
|
+
// VIMM
|
|
213
|
+
"www.vimm.tv",
|
|
214
|
+
// BrandNewTube
|
|
215
|
+
"brandnewtube.com",
|
|
216
|
+
// LBRY / Odysee
|
|
217
|
+
"lbry.tv",
|
|
218
|
+
"odysee.com",
|
|
219
|
+
// Skatehive / Skatehype
|
|
220
|
+
"ipfs.skatehive.app",
|
|
221
|
+
"www.skatehype.com",
|
|
222
|
+
"skatehype.com",
|
|
223
|
+
// archive.org
|
|
224
|
+
"archive.org",
|
|
225
|
+
// Truvvl
|
|
226
|
+
"embed.truvvl.com",
|
|
227
|
+
// Aureal
|
|
228
|
+
"aureal-embed.web.app",
|
|
229
|
+
"www.aureal-embed.web.app"
|
|
230
|
+
// Dapplr (player.*.dapplr.in / *.dapplr.in) — host suffix, handled below
|
|
231
|
+
]);
|
|
232
|
+
var ALLOWED_EMBED_HOST_SUFFIXES = [".dapplr.in"];
|
|
233
|
+
var EMBED_HOST_PATH_PATTERNS = {
|
|
234
|
+
"www.youtube.com": /^\/embed\//,
|
|
235
|
+
"youtube.com": /^\/embed\//,
|
|
236
|
+
"www.youtube-nocookie.com": /^\/embed\//,
|
|
237
|
+
"youtube-nocookie.com": /^\/embed\//,
|
|
238
|
+
"player.vimeo.com": /^\/video\//,
|
|
239
|
+
"player.twitch.tv": /^\/$/,
|
|
240
|
+
// channel/video carried in the query string
|
|
241
|
+
"emb.d.tube": /^\/$/,
|
|
242
|
+
// dtube carries the ref in the #! fragment
|
|
243
|
+
"play.3speak.tv": /^\/(watch|embed)/,
|
|
244
|
+
"open.spotify.com": /^\/embed\//,
|
|
245
|
+
"www.loom.com": /^\/embed\//,
|
|
246
|
+
"www.bitchute.com": /^\/embed\//,
|
|
247
|
+
"bitchute.com": /^\/embed\//,
|
|
248
|
+
"www.rumble.com": /^\/embed\//,
|
|
249
|
+
"rumble.com": /^\/embed\//,
|
|
250
|
+
"www.brighteon.com": /^\/embed\//,
|
|
251
|
+
"brighteon.com": /^\/embed\//
|
|
252
|
+
};
|
|
253
|
+
function isAllowedEmbedSrc(value) {
|
|
254
|
+
if (!value) return false;
|
|
255
|
+
let url;
|
|
256
|
+
try {
|
|
257
|
+
url = new URL(value.trim());
|
|
258
|
+
} catch {
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
if (url.protocol !== "https:") return false;
|
|
262
|
+
const host = url.hostname.toLowerCase();
|
|
263
|
+
const hostAllowed = ALLOWED_EMBED_HOSTS.has(host) || ALLOWED_EMBED_HOST_SUFFIXES.some((suffix) => host.endsWith(suffix));
|
|
264
|
+
if (!hostAllowed) return false;
|
|
265
|
+
const pathPattern = EMBED_HOST_PATH_PATTERNS[host];
|
|
266
|
+
if (pathPattern && !pathPattern.test(url.pathname)) return false;
|
|
267
|
+
return true;
|
|
268
|
+
}
|
|
179
269
|
function createParser() {
|
|
180
270
|
return new DOMParser$1({
|
|
181
271
|
onError(level, msg) {
|
|
@@ -571,6 +661,14 @@ function buildPictureSources(rawUrl) {
|
|
|
571
661
|
}
|
|
572
662
|
|
|
573
663
|
// src/methods/sanitize-html.method.ts
|
|
664
|
+
var EMBED_SRC_DATA_ATTRS = /* @__PURE__ */ new Set(["data-embed-src", "data-video-href"]);
|
|
665
|
+
var isSafeNavValue = (value) => {
|
|
666
|
+
const trimmed = value.trim().replace(/[\t\n\r\f\v\0]/g, "").toLowerCase();
|
|
667
|
+
if (!trimmed) return false;
|
|
668
|
+
const isSafeScheme = /^(https?|mailto|hive|tel|web\+[a-z0-9.+-]+):/i.test(trimmed);
|
|
669
|
+
const isRelative = /^(\/\/|\/[^/]?|#|\?|[a-z0-9._\-]+(\/|$))/i.test(trimmed);
|
|
670
|
+
return isSafeScheme || isRelative;
|
|
671
|
+
};
|
|
574
672
|
var decodeEntities = (input) => input.replace(/&#(\d+);?/g, (_, dec) => String.fromCodePoint(Number(dec))).replace(/&#x([0-9a-f]+);?/gi, (_, hex) => String.fromCodePoint(parseInt(hex, 16)));
|
|
575
673
|
var isProxyPSrcset = (srcset) => {
|
|
576
674
|
const base = trimTrailingSlash(getProxyBase());
|
|
@@ -598,6 +696,8 @@ function sanitizeHtml(html) {
|
|
|
598
696
|
if (tag === "video" && ["src", "poster"].includes(name) && !/^https?:\/\//.test(decodedLower)) return "";
|
|
599
697
|
if (tag === "img" && ["dynsrc", "lowsrc"].includes(name)) return "";
|
|
600
698
|
if (tag === "span" && name === "class" && decoded.toLowerCase().trim() === "wr") return "";
|
|
699
|
+
if (EMBED_SRC_DATA_ATTRS.has(name) && !isAllowedEmbedSrc(decoded)) return "";
|
|
700
|
+
if (name === "data-href" && !isSafeNavValue(decoded)) return "";
|
|
601
701
|
if (name === "id") {
|
|
602
702
|
if (!ID_WHITELIST.test(decoded)) return "";
|
|
603
703
|
}
|
|
@@ -1113,14 +1213,15 @@ function a(el, forApp, parentDomain = "ecency.com", seoContext, renderOptions) {
|
|
|
1113
1213
|
el.setAttribute("data-start-time", startTime);
|
|
1114
1214
|
}
|
|
1115
1215
|
if (renderOptions?.embedVideosDirectly) {
|
|
1216
|
+
const directSrc = `https://www.youtube.com/embed/${vid}`;
|
|
1116
1217
|
const wrapper = el.ownerDocument.createElement("span");
|
|
1117
1218
|
wrapper.setAttribute("class", "er-youtube-frame");
|
|
1118
1219
|
wrapper.setAttribute("style", "display:block");
|
|
1119
1220
|
const iframe2 = el.ownerDocument.createElement("iframe");
|
|
1120
1221
|
iframe2.setAttribute("class", "youtube-player");
|
|
1121
|
-
iframe2.setAttribute("src",
|
|
1222
|
+
iframe2.setAttribute("src", directSrc);
|
|
1122
1223
|
iframe2.setAttribute("title", "YouTube video");
|
|
1123
|
-
iframe2.setAttribute("allow", "accelerometer;
|
|
1224
|
+
iframe2.setAttribute("allow", "accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share");
|
|
1124
1225
|
iframe2.setAttribute("allowfullscreen", "");
|
|
1125
1226
|
wrapper.appendChild(iframe2);
|
|
1126
1227
|
el.appendChild(wrapper);
|
|
@@ -1257,12 +1358,13 @@ function a(el, forApp, parentDomain = "ecency.com", seoContext, renderOptions) {
|
|
|
1257
1358
|
el.textContent = "";
|
|
1258
1359
|
}
|
|
1259
1360
|
if (renderOptions?.embedVideosDirectly) {
|
|
1361
|
+
const directSrc = `${videoHref}&autoplay=false`;
|
|
1260
1362
|
const wrapper = el.ownerDocument.createElement("span");
|
|
1261
1363
|
wrapper.setAttribute("class", "er-speak-frame");
|
|
1262
1364
|
wrapper.setAttribute("style", "display:block");
|
|
1263
1365
|
const iframe2 = el.ownerDocument.createElement("iframe");
|
|
1264
1366
|
iframe2.setAttribute("class", "speak-iframe");
|
|
1265
|
-
iframe2.setAttribute("src",
|
|
1367
|
+
iframe2.setAttribute("src", directSrc);
|
|
1266
1368
|
iframe2.setAttribute("title", "3Speak video");
|
|
1267
1369
|
iframe2.setAttribute("allow", "accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share");
|
|
1268
1370
|
iframe2.setAttribute("allowfullscreen", "");
|
|
@@ -1375,7 +1477,7 @@ function a(el, forApp, parentDomain = "ecency.com", seoContext, renderOptions) {
|
|
|
1375
1477
|
}
|
|
1376
1478
|
|
|
1377
1479
|
// src/methods/iframe.method.ts
|
|
1378
|
-
function iframe(el, parentDomain = "ecency.com", forApp = false) {
|
|
1480
|
+
function iframe(el, parentDomain = "ecency.com", forApp = false, renderOptions) {
|
|
1379
1481
|
if (!el || !el.parentNode) {
|
|
1380
1482
|
return;
|
|
1381
1483
|
}
|
|
@@ -1418,7 +1520,12 @@ function iframe(el, parentDomain = "ecency.com", forApp = false) {
|
|
|
1418
1520
|
normalizedSrc = `${normalizedSrc}&mode=iframe`;
|
|
1419
1521
|
}
|
|
1420
1522
|
const hasAutoplay = /[?&]autoplay=/.test(normalizedSrc);
|
|
1421
|
-
let s
|
|
1523
|
+
let s;
|
|
1524
|
+
if (renderOptions?.embedVideosDirectly) {
|
|
1525
|
+
s = hasAutoplay ? normalizedSrc.replace(/([?&]autoplay=)[^&]*/i, "$1false") : `${normalizedSrc}&autoplay=false`;
|
|
1526
|
+
} else {
|
|
1527
|
+
s = hasAutoplay ? normalizedSrc : `${normalizedSrc}&autoplay=true`;
|
|
1528
|
+
}
|
|
1422
1529
|
if (forApp && !/[?&]layout=/.test(s)) {
|
|
1423
1530
|
s = `${s}&layout=mobile`;
|
|
1424
1531
|
}
|
|
@@ -1732,7 +1839,7 @@ function traverse(node, forApp, depth = 0, state = { firstImageFound: false }, p
|
|
|
1732
1839
|
a(child, forApp, parentDomain, seoContext, renderOptions);
|
|
1733
1840
|
}
|
|
1734
1841
|
if (child.nodeName.toLowerCase() === "iframe") {
|
|
1735
|
-
iframe(child, parentDomain, forApp);
|
|
1842
|
+
iframe(child, parentDomain, forApp, renderOptions);
|
|
1736
1843
|
}
|
|
1737
1844
|
if (child.nodeName === "#text") {
|
|
1738
1845
|
text(child, forApp);
|
|
@@ -2203,6 +2310,6 @@ function getPostBodySummary(obj, length, platform) {
|
|
|
2203
2310
|
return res;
|
|
2204
2311
|
}
|
|
2205
2312
|
|
|
2206
|
-
export { IMAGE_SIZES, SECTION_LIST, buildPictureSources, buildSrcSet, buildSrcSetForFormat, catchPostImage, getEntryImageRawUrl, isPictureEligibleRawUrl, isValidPermlink, getPostBodySummary as postBodySummary, proxifyImageSrc, markdown2Html as renderPostBody, setCacheSize, setProxyBase, setSlowRenderThresholdMs, simpleMarkdownToHTML };
|
|
2313
|
+
export { IMAGE_SIZES, SECTION_LIST, buildPictureSources, buildSrcSet, buildSrcSetForFormat, catchPostImage, getEntryImageRawUrl, isAllowedEmbedSrc, isPictureEligibleRawUrl, isValidPermlink, getPostBodySummary as postBodySummary, proxifyImageSrc, markdown2Html as renderPostBody, setCacheSize, setProxyBase, setSlowRenderThresholdMs, simpleMarkdownToHTML };
|
|
2207
2314
|
//# sourceMappingURL=index.js.map
|
|
2208
2315
|
//# sourceMappingURL=index.js.map
|